jni小细节操作

  1. 数组的细节处理
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    #include <jni.h>
    #include <string>
    #include <stdlib.h>

    int compare(const jint *a, const jint *b) {
    return *a - *b;
    }


    extern "C"
    JNIEXPORT void JNICALL
    Java_com_zzw_jnidemo_ArrayUtils_sort(JNIEnv *env, jclass type, jintArray array_) {
    jint *intArray = env->GetIntArrayElements(array_, NULL);
    jsize length = env->GetArrayLength(array_);

    //排序
    //void *ptr 指向待排序的数组的指针
    //size_t count 数组的元素数目
    //size_t size 数组每个元素的字节大小
    // int (*comp)(const void *, const void *) 比较函数。若首个参数小于第二个,则返回负整数值,若首个参数大于第二个,
    // 则返回正整数值,若两参数相等,则返回零。
    // void qsort( void *ptr, size_t count, size_t size,int (*comp)(const void *, const void *) );

    qsort(intArray, static_cast<size_t>(length), sizeof(int),
    reinterpret_cast<int (*)(const void *, const void *)>(compare));

    //mode =
    //0 表示 既要同步数据给jArray,又要释放intArray
    //JNI_COMMIT : copy content, do not free buffer ,会同步数据给jArray,但是不会释放intArray
    //JNI_ABORT : free buffer w/o copying back ,不会同步数据给jArray,但是会释放intArray
    env->ReleaseIntArrayElements(array_, intArray, JNI_ABORT);
    }

2.局部引用和全局引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//局部变量
extern "C"
JNIEXPORT void JNICALL
Java_com_zzw_jnidemo_ArrayUtils_localRef(JNIEnv *env, jclass type) {

//在Native层侯建的Java对象,不用了该怎么管理? 需要手动回收
//在Java层开辟的内存由谁管理(JavaGC管理),能开辟多大内存?

//字符串截取
jclass str_clz = env->FindClass("java/lang/String");
jmethodID init_str = env->GetMethodID(str_clz, "<init>", "()V");
jobject j_str = env->NewObject(str_clz, init_str);
//.....

//jobject不使用了,需要回收。删除了就不能使用了
env->DeleteLocalRef(j_str);
}



//全局变量,需要在合适的时机去释放 env->DeleteGlobalRef(globalStr);删除之后不能使用
jstring globalStr;
extern "C"
JNIEXPORT void JNICALL
Java_com_zzw_jnidemo_ArrayUtils_saveGlobalRef(JNIEnv *env, jclass type, jstring str_) {
globalStr = static_cast<jstring>(env->NewGlobalRef(str_));
}
extern "C"
JNIEXPORT jstring JNICALL
Java_com_zzw_jnidemo_ArrayUtils_getStrGlobalRef(JNIEnv *env, jclass type) {

return globalStr;
}

3.静态缓存策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//局部静态缓存
extern "C"
JNIEXPORT void JNICALL
Java_com_zzw_jnidemo_ArrayUtils_staticLcalCache(JNIEnv *env, jclass type, jstring name_) {
// jfieldID f_id =NULL;
static jfieldID f_id = NULL;//局部缓存 ,这个方法会被多次调用,不需要反复去获取jfieldID
if (!f_id) {
f_id = env->GetStaticFieldID(type, "name", "Ljava/lang/String;");
} else {
LOGE("f_id不为null");//加上static将会缓存
}
env->SetStaticObjectField(type, f_id, name_);
}

//全局静态缓存

static jfieldID jfieldID1;
static jfieldID jfieldID2;
static jfieldID jfieldID3;
//一般初始化的时候调用
extern "C"
JNIEXPORT void JNICALL
Java_com_zzw_jnidemo_ArrayUtils_initStatic(JNIEnv *env, jclass type) {
jfieldID1 = env->GetStaticFieldID(type, "name", "Ljava/lang/String;");
jfieldID2 = env->GetStaticFieldID(type, "name1", "Ljava/lang/String;");
jfieldID3 = env->GetStaticFieldID(type, "name2", "Ljava/lang/String;");
}

//如果反复调用设置值得函数,那么将不会每次都获取id
void setData(JNIEnv *env, jclass jclazz, jobject object) {
env->SetStaticObjectField(jclazz, jfieldID1, object);
env->SetStaticObjectField(jclazz, jfieldID2, object);
env->SetStaticObjectField(jclazz, jfieldID3, object);
}

4.异常处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

extern "C"
JNIEXPORT void JNICALL
Java_com_zzw_jnidemo_ArrayUtils_exception2(JNIEnv *env, jclass cls) {
jthrowable exc = NULL;
jmethodID mid = env->GetStaticMethodID(cls, "exceptionCallback", "()V");
if (mid != NULL) {
env->CallStaticVoidMethod(cls, mid);
}
LOGE("In C: Java_com_zzw_jnidemo_ArrayUtils_exception2-->called!!!!");
//这种也是可以检测的
// exc = env->ExceptionOccurred(); // 返回一个指向当前异常对象的引用
// if (exc) {

if (env->ExceptionCheck()) { // 检查JNI调用是否有引发异常
env->ExceptionDescribe();//打印错误信息
env->ExceptionClear(); // 清除引发的异常,在Java层不会打印异常的堆栈信息
env->ThrowNew(env->FindClass("java/lang/Exception"), "JNI抛出的异常!");
return;
}
//补救 。。。
mid = env->GetStaticMethodID(cls, "normalCallback", "()V");
if (mid != NULL) {
env->CallStaticVoidMethod(cls, mid);
}
}



@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

try {
ArrayUtils.exception();
} catch (Exception e) {
e.printStackTrace();//打印抛出的异常
}
}

  1. c++调用java: 这里需要分为在主线程调用还是在子线程调用,在主线程调用就直接通过函数的env调用即可,在子线程调用则需要JavaVM ->AttachCurrentThread拿到env然后调用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42


#include "JavaListener.h"
JavaVM *jvm;

JavaListener *javaListener;

pthread_t chidlThread;


void *childCallback(void *data)
{
JavaListener *javaListener1 = (JavaListener *) data;
//子线程调用
javaListener1->onError(0, 101, "c++ call java meid from child thread!");
pthread_exit(&chidlThread);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_zzw_jnithread_ThreadDemo_callbackFromC(JNIEnv *env, jobject instance) {

javaListener = new JavaListener(jvm, env, env->NewGlobalRef(instance));
//主线程调用
//javaListener->onError(1, 100, "c++ call java meid from main thread!");
//子线程调用
pthread_create(&chidlThread, NULL, childCallback, javaListener);

}



JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void* reserved)
{
JNIEnv *env;
jvm = vm;
if(vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK)
{
return -1;
}
return JNI_VERSION_1_6;
}

JavaListener.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

#include "jni.h"

#ifndef JNITHREAD_JAVALISTENER_H
#define JNITHREAD_JAVALISTENER_H


class JavaListener {

public:
JavaVM *jvm;
_JNIEnv *jenv;
jobject jobj;
jmethodID jmid;
public:
JavaListener(JavaVM *vm, _JNIEnv *env, jobject obj);
~JavaListener();

/**
* 1:主线程
* 0:子线程
* @param type
* @param code
* @param msg
*/
void onError(int type, int code, const char *msg);


};


#endif //JNITHREAD_JAVALISTENER_H

JavaListener.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

#include "JavaListener.h"


void JavaListener::onError(int type, int code, const char *msg) {

if(type == 0)
{
JNIEnv *env;
jvm->AttachCurrentThread(&env, 0);
jstring jmsg = env->NewStringUTF(msg);
env->CallVoidMethod(jobj, jmid, code, jmsg);
env->DeleteLocalRef(jmsg);

jvm->DetachCurrentThread();


}
else if(type == 1)
{
jstring jmsg = jenv->NewStringUTF(msg);
jenv->CallVoidMethod(jobj, jmid, code, jmsg);
jenv->DeleteLocalRef(jmsg);
}
}

JavaListener::JavaListener(JavaVM *vm, _JNIEnv *env, jobject obj) {

jvm = vm;
jenv = env;
jobj = obj;

jclass clz = env->GetObjectClass(jobj);
if(!clz)
{
return;
}
jmid = env->GetMethodID(clz, "onError", "(ILjava/lang/String;)V");
if(!jmid)
return;


}

ThreadDemo.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class ThreadDemo {

static
{
System.loadLibrary("native-lib");
}

private OnErrerListener onErrerListener;

public void setOnErrerListener(OnErrerListener onErrerListener) {
this.onErrerListener = onErrerListener;
}

public void onError(int code, String msg)
{
if(onErrerListener != null)
{
onErrerListener.onError(code, msg);
}
}

public interface OnErrerListener
{
void onError(int code, String msg);
}

public native void callbackFromC();

}

-------------The End-------------